﻿//-----------------------------------------------------------------------
// <copyright file="ValidateChanges.cs" company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation 2011. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Internal.TeamFoundationServer.Submit
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    using System.Threading;
    using System.Windows.Forms;
    using Microsoft.TeamFoundation;
    using Microsoft.TeamFoundation.Build.Client;
    using Microsoft.TeamFoundation.Client;
    using Microsoft.TeamFoundation.Common;
    using Microsoft.TeamFoundation.Framework.Client;
    using Microsoft.TeamFoundation.Lab.Client;
    using Microsoft.TeamFoundation.TestManagement.Client;
    using Microsoft.TeamFoundation.VersionControl.Client;
    using Properties;

    /// <summary>
    /// ValidateChanges class.
    /// </summary>
    internal partial class ValidateChanges : WizardControlTemplate
    {
        /// <summary>
        /// List of shelve set objects.
        /// </summary>
        private List<Shelveset> shelvesets;

        /// <summary>
        /// Build server object.
        /// </summary>
        private IBuildServer buildServer;

        /// <summary>
        /// List of Build definitions.
        /// </summary>
        private IBuildDefinition[] buildDefinitions;

        /// <summary>
        /// Lab Validation object.
        /// </summary>
        private LabValidation labValidation;

        /// <summary>
        /// Initializes a new instance of the <see cref="ValidateChanges"/> class.
        /// </summary>
        public ValidateChanges()
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.InitializeComponent();

                this.cmbTFSServer.Items.Clear();
                foreach (var item in TeamFoundationHelper.GetRegisteredCollectionNames())
                {
                    this.cmbTFSServer.Items.Add(item);
                }

                Utilities.FixComboItemsWidth(this, this.cmbTFSServer);

                if (this.cmbTFSServer.Items.Count > 0)
                {
                    Utilities.GetDefaultValue(this.cmbTFSServer, Settings.Default.TfsSourceServer);
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Update window.
        /// </summary>
        /// <param name="title">Window Title.</param>
        /// <param name="description">Window Description.</param>
        /// <param name="backButtonEnabled">Back Button Enabled?</param>
        /// <param name="nextButtonEnabled">Next Button Enabled?</param>
        /// <param name="nextButtonCaption">Next Button Caption.</param>
        /// <param name="action">Action to perform on control.</param>
        public override void UpdateWindow(Label title, RichTextBox description, out bool backButtonEnabled, out bool nextButtonEnabled, out string nextButtonCaption, object action)
        {
            backButtonEnabled = true;
            nextButtonEnabled = this.gridBuilds.SelectedRows.Count > 0;
            nextButtonCaption = Resources.ValidationButtonCaption;

            title.Text = Resources.ValidationTitle;
            description.Text = Resources.ValidationDescription;
        }

        /// <summary>
        /// Button next event.
        /// </summary>
        public override void NextStep()
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                /*
                if (this.LabRun())
                {
                    return;
                }
                 * */

                var buildInParallel = bool.Parse(this.gridBuilds.SelectedRows[0].Cells["colParallel"].Value.ToString());
                var incrementalBuild = bool.Parse(this.gridBuilds.SelectedRows[0].Cells["colIncremental"].Value.ToString());
                var queuedBuild = (from buildDefinition in this.buildDefinitions
                                   where buildDefinition.Name == this.gridBuilds.SelectedRows[0].Cells["colName"].Value.ToString()
                                   select TeamFoundationHelper.ValidateChanges(this.buildServer, buildDefinition, this.cmbShelveset.Text, buildInParallel, incrementalBuild)).FirstOrDefault();
                
                if (queuedBuild == null)
                {
                    return;
                }

                Debug.Assert(queuedBuild != null, "queuedBuild should never be null");

                if (queuedBuild.Build == null)
                {
                    // TODO Message box with Error
                    return;
                }

                try
                {
                    var userToken = Environment.UserName.Split('\\');
                    var user = (userToken.Length > 1) ? userToken[1] : userToken[0];
                    user = user.Replace("-", string.Empty).ToUpperInvariant();
                    queuedBuild.Build.BuildNumber += " " + user + " WS=" + this.cmbWorkspace.Text + " - SS=" + this.cmbShelveset.Text;

                    if (queuedBuild.Build.BuildNumber.Length > 64)
                    {
                        queuedBuild.Build.BuildNumber = queuedBuild.Build.BuildNumber.Substring(0, 64);
                    }

                    queuedBuild.Build.Save();
                }
                catch (Microsoft.TeamFoundation.Build.Client.AccessDeniedException)
                {
                    // Do nothing, we can ignore the error here.
                }
                catch (BuildServerException e)
                {
                    MessageBox.Show(
                        e.Message,
                        Resources.ErrorString,
                        MessageBoxButtons.OK,
                        MessageBoxIcon.Error,
                        MessageBoxDefaultButton.Button1,
                        MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
                }

                queuedBuild.Build.RefreshAllDetails();
                queuedBuild.WaitForBuildStart();
                
                var tswaClientHyperlinkService = TeamFoundationHelper.GetTfsTeamProjectCollection(this.cmbTFSServer.Text).GetService<TswaClientHyperlinkService>();
                var uri = tswaClientHyperlinkService.GetViewBuildDetailsUrl(queuedBuild.Build.Uri);

                // Open Code Review
                using (var process = new Process())
                {
                    process.StartInfo.FileName = uri.ToString();
                    process.Start();
                }
                
                // Schedule an alert
                var eventService = TeamFoundationHelper.GetTfsTeamProjectCollection(this.cmbTFSServer.Text).GetService<IEventService>();
                var eventSubscriptions = eventService.GetEventSubscriptions(Environment.UserDomainName + "\\" + Environment.UserName).ToList();
                if (eventSubscriptions.Count > 0)
                {
                    // Remove old subscriptions
                    foreach (var evt in eventSubscriptions.Where(evt => evt.EventType == "BuildCompletionEvent" && evt.Tag.Contains("Build") && evt.Tag.Contains("completed")))
                    {
                        if (evt.DeliveryPreference == null || !evt.DeliveryPreference.Address.Contains(Environment.UserName))
                        {
                            continue;
                        }

                        const string Build = "Build:";
                        var positionBuild = evt.Tag.IndexOf(Build, System.StringComparison.Ordinal);

                        if (positionBuild == -1)
                        {
                            continue;
                        }

                        var buildNumber = evt.Tag.Substring(positionBuild + Build.Length);

                        const string Completed = " completed.";
                        var positionCompleted = buildNumber.IndexOf(Completed, System.StringComparison.Ordinal);

                        if (positionCompleted == -1)
                        {
                            continue;
                        }

                        buildNumber = buildNumber.Substring(0, positionCompleted);

                        // Query status of the build
                        var spec = this.buildDefinitions[0].BuildServer.CreateBuildDetailSpec(this.buildDefinitions[0].TeamProject);
                        spec.MaxBuildsPerDefinition = 5;
                        spec.BuildNumber = buildNumber;
                        spec.InformationTypes = null;
                        var builds = this.buildDefinitions[0].BuildServer.QueryBuilds(spec).Builds;
                        if (builds.Length <= 0)
                        {
                            eventService.UnsubscribeEvent(evt.ID);
                            continue;
                        }

                        if (builds[0].Status != BuildStatus.Failed && builds[0].Status != BuildStatus.PartiallySucceeded
                            && builds[0].Status != BuildStatus.Stopped && builds[0].Status != BuildStatus.Succeeded)
                        {
                            continue;
                        }

                        if (builds[0].FinishTime.ToUniversalTime().AddHours(1) < DateTime.UtcNow)
                        {
                            eventService.UnsubscribeEvent(evt.ID);
                        }
                    }
                }

                var deliveryPreference = new DeliveryPreference
                                             {
                                                 Type = DeliveryType.EmailHtml,
                                                 Schedule = DeliverySchedule.Immediate,
                                                 Address = Environment.UserName + "@" + TfsSubmitConfig.MailDomainName,
                                             };

                var classification = string.Format(
                    CultureInfo.InvariantCulture,
                    "TeamProject = '{0}' AND BuildNumber = '{1}'",
                    queuedBuild.TeamProject,
                    queuedBuild.Build.BuildNumber);

                eventService.SubscribeEvent(
                    Environment.UserDomainName + "\\" + Environment.UserName,
                    "BuildCompletionEvent",
                    classification,
                    deliveryPreference,
                    string.Format(CultureInfo.InvariantCulture, "<PT N=\"{0}: Build:{1} completed.\" />", queuedBuild.TeamProject, queuedBuild.Build.BuildNumber));

                MessageBox.Show(
                    Resources.ValidationInProgressString,
                    Resources.InformationString,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Information,
                    MessageBoxDefaultButton.Button1,
                    MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);

                this.FireGoHomeRequested(WizardMainMenuControl.MainMenuAction.StartCodeReview /* after validate navigate to code review */);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Run is a Lab run
        /// </summary>
        /// <returns>True for lab run, otherwise false.</returns>
        private bool LabRun()
        {
            // Get the pending changes. Use to get project name.
            var pendingChanges = TeamFoundationHelper.GetPendingChangesByShelveSetName(this.cmbTFSServer.Text, this.cmbWorkspace.Text, this.cmbShelveset.Text);

            var serverItem = string.Empty;
            if (pendingChanges != null && pendingChanges.Length > 0)
            {
                serverItem = pendingChanges[0].ServerItem;
            }

            var testManagementService = TeamFoundationHelper.GetTfsTeamProjectCollection(this.cmbTFSServer.Text).GetService(typeof(ITestManagementService)) as ITestManagementService;
            var labService = TeamFoundationHelper.GetTfsTeamProjectCollection(this.cmbTFSServer.Text).GetService(typeof(LabService)) as LabService;
            var projectName = TeamFoundationHelper.GetTeamProjectNameByLocalItem(this.cmbTFSServer.Text, this.cmbWorkspace.Text, serverItem);
            var testManagementTeamProject = testManagementService.GetTeamProject(projectName);
            var testPlans =
                testManagementTeamProject.TestPlans.Query("Select * From TestPlan").Where(
                    p =>
                    p.Name.StartsWith("TFSSUBMIT -", StringComparison.OrdinalIgnoreCase)
                    && p.EndDate < DateTime.UtcNow.AddDays(-1));

            foreach (var plan in testPlans)
            {
                plan.Delete();
            }

            var buildDef = this.buildDefinitions.Where(b => b.Name == this.gridBuilds.SelectedRows[0].Cells["colName"].Value.ToString()).ToList();
            if (buildDef[0] != null && buildDef[0].Process != null && buildDef[0].Process.ServerPath.Contains("Lab"))
            {
                // Does this Team Project has Lab configure?
                if (!labService.IsLabConfigured(projectName))
                {
                    return true;
                }

                Debug.Assert(this.labValidation == null || this.labValidation.IsDisposed, "BUGBUG labValidation should be null or not disposed at this point.");

                this.labValidation = new LabValidation();
                this.labValidation.BuildServer = this.buildServer;
                this.labValidation.BuildDefinitions = this.buildDefinitions;
                this.labValidation.LabService = labService;
                this.labValidation.TestManagementService = testManagementService;
                this.labValidation.ProjectName = projectName;
                this.labValidation.TestManagementTeamProject = testManagementTeamProject;
                this.FireOpenControl(this.labValidation);

                return true;
            }

            return false;
        }

        /// <summary>
        /// Select TFS Collection.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbTfsServerSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.cmbWorkspace.Items.Clear();
                this.cmbShelveset.Items.Clear();
                this.gridBuilds.Rows.Clear();
                this.buildDefinitions = null;
                this.textDescription.Text = string.Empty;
                this.shelvesets = null;

                if (this.cmbTFSServer.Text.Length == 0)
                {
                    return;
                }

                Utilities.SetDefaultValue("TfsSourceServer", this.cmbTFSServer.Text);

                // Retrieve User workspaces for specific project collection.
                foreach (var item in TeamFoundationHelper.GetWorkspaceNames(this.cmbTFSServer.Text))
                {
                    this.cmbWorkspace.Items.Add(item);
                }

                Utilities.FixComboItemsWidth(this, this.cmbWorkspace);

                if (this.cmbWorkspace.Items.Count > 0)
                {
                    Utilities.GetDefaultValue(this.cmbWorkspace, Settings.Default.TfsSourceWorkspace);
                }
            }
            catch (TeamFoundationServiceUnavailableException err)
            {
                MessageBox.Show(
                   err.Message,
                   Resources.UnhandledException,
                   MessageBoxButtons.OK,
                   MessageBoxIcon.Error,
                   MessageBoxDefaultButton.Button1,
                   MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// Select Workspace.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbWorkspaceSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.cmbShelveset.Items.Clear();
                this.gridBuilds.Rows.Clear();
                this.buildDefinitions = null;
                this.textDescription.Text = string.Empty;

                if (this.cmbWorkspace.Text.Length == 0)
                {
                    return;
                }

                Utilities.SetDefaultValue("TfsSourceWorkspace", this.cmbWorkspace.Text);

                this.shelvesets = TeamFoundationHelper.GetShelvesets(this.cmbTFSServer.Text, this.cmbWorkspace.Text);
                this.shelvesets.ForEach(shelveset => this.cmbShelveset.Items.Add(shelveset.Name));
                Utilities.FixComboItemsWidth(this, this.cmbShelveset);

                if (this.cmbShelveset.Items.Count > 0)
                {
                    this.cmbShelveset.SelectedIndex = 0;
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }
        
        /// <summary>
        /// Select Change list.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void CmbShelvesetSelectedIndexChanged(object sender, EventArgs e)
        {
            try
            {
                Cursor.Current = Cursors.WaitCursor;

                this.textDescription.Text = string.Empty;
                this.buildDefinitions = null;
                this.gridBuilds.Rows.Clear();

                if (this.cmbShelveset.Text.Length == 0)
                {
                    return;
                }

                var shelveset = this.shelvesets.Find(item => item.Name == this.cmbShelveset.Text);
                if (shelveset != null)
                {
                    this.textDescription.Text = shelveset.Comment;
                }

                // Get the pending changes. USed to get which build definitions are affected.
                var pendingChanges = TeamFoundationHelper.GetPendingChangesByShelveSetName(
                    this.cmbTFSServer.Text,
                    this.cmbWorkspace.Text,
                    this.cmbShelveset.Text);

                if (pendingChanges == null || pendingChanges.Length <= 0)
                {
                    return;
                }

                this.buildServer = TeamFoundationHelper.GetTfsTeamProjectCollection(this.cmbTFSServer.Text).GetService<IBuildServer>();
                this.buildDefinitions = this.buildServer.GetAffectedBuildDefinitions(new[] { pendingChanges[0].ServerItem });
                foreach (var buildDefinition in this.buildDefinitions.Where(buildDefinition => buildDefinition.TriggerType == DefinitionTriggerType.None
                                                                                               || buildDefinition.TriggerType == DefinitionTriggerType.BatchedContinuousIntegration
                                                                                               || buildDefinition.TriggerType == DefinitionTriggerType.GatedCheckIn))
                {
                    this.gridBuilds.Rows.Add(
                        buildDefinition.Name,
                        buildDefinition.Description,
                        buildDefinition.ContinuousIntegrationType.ToString(),
                        buildDefinition.QueueStatus,
                        true,
                        true);
                }
            }
            finally
            {
                Cursor.Current = Cursors.Default;
            }
        }

        /// <summary>
        /// GridBuilds Selection Changed event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        private void GridBuildsSelectionChanged(object sender, EventArgs e)
        {
            this.FireChangeContinueButtonEnabled(this.gridBuilds.SelectedRows.Count > 0);
        }
    }
}
